package com.jayden.scalevideoplayer.videoplayer;

import android.animation.Animator;
import android.animation.AnimatorListenerAdapter;
import android.animation.ObjectAnimator;
import android.animation.ValueAnimator;
import android.content.Context;
import android.content.res.Configuration;
import android.graphics.Bitmap;
import android.graphics.RectF;
import android.graphics.SurfaceTexture;
import android.util.AttributeSet;
import android.util.Log;
import android.view.GestureDetector;
import android.view.Gravity;
import android.view.MotionEvent;
import android.view.ScaleGestureDetector;
import android.view.TextureView;
import android.view.animation.LinearInterpolator;
import android.widget.FrameLayout;
import android.widget.ImageView;

import androidx.annotation.DrawableRes;

import java.util.concurrent.TimeUnit;

/**
 * 基于TextureView实现的视频播放类，实现了放大缩小移动功能。
 * 备注：单一职责，此类的职责只负责与View相关的操作；
 * 播放控制等在{@link VideoControl}实例类中处理。
 * <p>
 * Created by LiuYanBing
 */
public class VideoPlayerView extends FrameLayout implements TextureView.SurfaceTextureListener {
    private static final String TAG = "VideoPlayerView";

    private static final float DOUBLE_TAP_SCALE_DEFAULT = 2.0f;//双击放大的默认倍数（已填充父View的情况下）
    private static final float MIN_SCALE_FACTOR = 1;//最小缩放的比例，目前缩放不能小于原视图。
    private OnVideoPlayerViewListener mListener;
    private SurfaceTexture mSurface;
    private boolean scaleDisable, doubleTapScaleDisable, scrollDisable;
    private GestureDetector mGestureDetector;
    private ScaleGestureDetector mScaleGestureDetector;
    private float doubleTapScaleFactor = Float.NaN;
    private long doubleTapScaleDuration = TimeUnit.MILLISECONDS.toMillis(300);
    private float maxScaleFactor = 4.5f;
    private float currentScaleFactor = 1;
    private TextureView mTextureView;
    private ImageView mIvPlaceholder;
    private LayoutParams mInternalLayoutParams;
    private boolean isScaling;
    private boolean isOrientationChanged = true;

    public VideoPlayerView(Context context) {
        super(context);
        init(context);
    }

    public VideoPlayerView(Context context, AttributeSet attrs) {
        super(context, attrs);
        init(context);
    }

    private void init(Context context) {
        initInternalView(context);
        initGestureDetector(context);
    }

    /**
     * 设置是否禁止双指缩放
     */
    public void setScaleDisable(boolean disable) {
        this.scaleDisable = disable;
    }

    /**
     * 设置是否禁止双击缩放
     */
    public void setDoubleTapScaleDisable(boolean disable) {
        this.doubleTapScaleDisable = disable;
    }

    /**
     * 设置双击缩放时的动画时长
     */
    public void setDoubleTapScaleDuration(long duration) {
        this.doubleTapScaleDuration = duration;
    }

    /**
     * 设置是否禁止拖动
     */
    public void setScrollDisable(boolean disable) {
        this.scrollDisable = disable;
    }

    /**
     * 设置最大可缩放的比例
     */
    public void setMaxScale(float scale) {
        this.maxScaleFactor = scale;
    }

    /**
     * 设置双击放大时的放大比例
     */
    public void setDoubleTapScale(float scale) {
        this.doubleTapScaleFactor = scale;
    }

    /**
     * 设置播放视图的LayoutParams
     */
    public void setInternalLayoutParams(LayoutParams params) {
        mInternalLayoutParams = params;
        mTextureView.setLayoutParams(params);
        mIvPlaceholder.setLayoutParams(params);
    }

    /**
     * 获取播放视图的LayoutParams
     */
    public LayoutParams getInternalLayoutParams() {
        return mInternalLayoutParams;
    }

    /**
     * 添加控件回调
     */
    public void setVideoPlayerViewListener(OnVideoPlayerViewListener listener) {
        this.mListener = listener;
        if (mListener == null) {
            return;
        }
        if (mSurface != null) {
            mListener.onSurfaceTextureAvailable(mSurface);
        }
    }

    /**
     * 设置占位图的可见属性
     */
    public void setPlaceholderVisibility(int visibility) {
        mIvPlaceholder.setVisibility(visibility);
    }

    /**
     * 设置占位图
     */
    public void setPlaceholderImage(Bitmap bitmap) {
        mIvPlaceholder.setImageBitmap(bitmap);
    }

    public void setPlaceholderImage(@DrawableRes int resId) {
        mIvPlaceholder.setImageResource(resId);
    }

    public Bitmap getTextureBitmap() {
        return mTextureView.getBitmap();
    }

    /**
     * 重置缩放视图为初始值
     */
    public final void resetInternal() {
        Log.d(TAG, "resetInternal()");
        setInternalScale(1);
        resetInternalPivot();
    }

    private void initInternalView(Context context) {
        if (mInternalLayoutParams == null) {
            mInternalLayoutParams = new LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT);
            mInternalLayoutParams.gravity = Gravity.CENTER;
        }
        mTextureView = new TextureView(context);
        mTextureView.setSurfaceTextureListener(this);
        mIvPlaceholder = new ImageView(context);
        addView(mIvPlaceholder, mInternalLayoutParams);
        addView(mTextureView, mInternalLayoutParams);
    }

    private void initGestureDetector(Context context) {
        mGestureDetector = new GestureDetector(context, new MyGestureListener());
        mScaleGestureDetector = new ScaleGestureDetector(context, new MyScaleGestureListener());
    }

    private void setInternalPivot(float x, float y) {
        Log.d(TAG, "setInternalPivot x=" + x + " y=" + y);
        if (Float.isNaN(x) || Float.isNaN(y)
                || Float.isInfinite(x) || Float.isInfinite(y)) {
            return;
        }
        mTextureView.setPivotX(x);
        mTextureView.setPivotY(y);
        mIvPlaceholder.setPivotX(x);
        mIvPlaceholder.setPivotY(y);
    }

    private void setInternalScale(float scale) {
        Log.d(TAG, "setInternalScale scale=" + scale);
        if (Float.isNaN(scale) || Float.isNaN(scale)) {
            return;
        }
        mTextureView.setScaleX(scale);
        mTextureView.setScaleY(scale);
        mIvPlaceholder.setScaleX(scale);
        mIvPlaceholder.setScaleY(scale);
    }

    private void resetInternalPivot() {
        setInternalPivot(mTextureView.getWidth() / 2.0f, mTextureView.getHeight() / 2.0f);
    }

    @Override
    protected void onConfigurationChanged(Configuration newConfig) {
        super.onConfigurationChanged(newConfig);
        isOrientationChanged = true;
    }

    @Override
    protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
        super.onLayout(changed, left, top, right, bottom);
        if (isOrientationChanged) {
            resetInternal();
            isOrientationChanged = false;
        }
    }

    private boolean lastDoublePointer;//优化双指缩放结束后还会触发拖动事件的问题

    @Override
    public boolean onTouchEvent(MotionEvent event) {
        if ((!scrollDisable || !doubleTapScaleDisable) && event.getPointerCount() == 1
                && !lastDoublePointer && mGestureDetector.onTouchEvent(event)) {
            return true;//响应了拖动或双击事件
        }

        if (!scaleDisable && event.getPointerCount() == 2 && mScaleGestureDetector.onTouchEvent(event)) {
            lastDoublePointer = true;
            return true;//响应了双指缩放事件
        }
        if (event.getAction() == MotionEvent.ACTION_UP) {
            lastDoublePointer = false;
        }
        return true;
    }

    @Override
    public boolean onInterceptTouchEvent(MotionEvent ev) {
        //目前的业务中子View不需要处理任何Touch事件，所以在这里直接拦截。
        return true;
    }

    @Override
    public void onSurfaceTextureAvailable(SurfaceTexture surface, int width, int height) {
        mSurface = surface;
        if (mListener != null) {
            mListener.onSurfaceTextureAvailable(surface);
        }
    }

    @Override
    public void onSurfaceTextureSizeChanged(SurfaceTexture surface, int width, int height) {
    }

    @Override
    public boolean onSurfaceTextureDestroyed(SurfaceTexture surface) {
        if (mListener != null) {
            mListener.onSurfaceTextureDestroyed(surface);
        }
        return true;
    }

    @Override
    public void onSurfaceTextureUpdated(SurfaceTexture surface) {
        if (mListener != null) {
            mListener.onSurfaceTextureUpdated();
        }
    }

    /**
     * 负责双指缩放相关触发逻辑
     */
    private class MyScaleGestureListener extends ScaleGestureDetector.SimpleOnScaleGestureListener {
        @Override
        public boolean onScaleBegin(ScaleGestureDetector detector) {
            Log.d(TAG, "onScaleBegin()");
            isScaling = true;
            return true;
        }

        @Override
        public boolean onScale(ScaleGestureDetector detector) {
            Log.d(TAG, "onScale() ScaleFactor=" + detector.getScaleFactor());
            float scale = currentScaleFactor * detector.getScaleFactor();
            if (scale < MIN_SCALE_FACTOR) {
                scale = MIN_SCALE_FACTOR;
            } else if (scale > maxScaleFactor) {
                scale = maxScaleFactor;
            }
            setInternalScale(scale);
            return false;
        }

        @Override
        public void onScaleEnd(ScaleGestureDetector detector) {
            Log.d(TAG, "onScaleEnd()");
            currentScaleFactor = Math.max(mTextureView.getScaleX(), mTextureView.getScaleY());
            if (currentScaleFactor == 1) {
                setInternalPivot(mTextureView.getWidth() / 2f, mTextureView.getHeight() / 2f);
            }
            isScaling = false;
        }
    }

    /**
     * 负责单击，拖动相关逻辑
     */
    private class MyGestureListener extends GestureDetector.SimpleOnGestureListener {
        private RectF srcRect = new RectF();
        private RectF dstRect = new RectF();
        private ObjectAnimator scaleAnimator;

        @Override
        public boolean onDown(MotionEvent e) {
            srcRect.set(0, 0, mTextureView.getWidth(), mTextureView.getHeight());
            currentScaleFactor = mTextureView.getScaleX();
            return super.onDown(e);
        }

        /**
         * 触发了单击事件，这个方法的回调说明已经排除了不会触发双击事件
         */
        @Override
        public boolean onSingleTapConfirmed(MotionEvent e) {
            Log.d(TAG, "onSingleTapConfirmed()");
            performClick();
            return true;
        }

        /**
         * 触发了双击事件
         */
        @Override
        public boolean onDoubleTap(MotionEvent e) {
            if (doubleTapScaleDisable) {
                return false;
            }
            if (scaleAnimator != null && scaleAnimator.isRunning()) {
                return true;
            }
            Log.d(TAG, "onDoubleTap()");
            float doubleTapScale = computeDoubleTapScale();
            if (currentScaleFactor < doubleTapScale) {
                createScaleAnimator(currentScaleFactor, doubleTapScale);
            } else {
                createScaleAnimator(currentScaleFactor, 1.0f);
            }
            scaleAnimator.start();
            return true;
        }

        /**
         * 触发了拖动事件
         */
        @Override
        public boolean onScroll(MotionEvent e1, MotionEvent e2, float distanceX, float distanceY) {
            if (isScaling || scrollDisable) return false;
            Log.d(TAG, "onScroll拖动() distanceX=" + distanceX + " distanceY=" + distanceY);
            if (mTextureView.getMatrix().mapRect(dstRect, srcRect)) {
                setInternalPivot(checkScrollX(dstRect, distanceX), checkScrollY(dstRect, distanceY));
            }
            return true;
        }

        private float checkScrollX(RectF rect, float distanceX) {
            if (rect.width() <= getWidth() || distanceX == 0) {
                return mTextureView.getPivotX();
            }
            if (distanceX < 0) {
                if (rect.left - distanceX > 0)
                    distanceX = rect.left;
            } else if (distanceX > 0) {
                if (rect.right - distanceX < mTextureView.getWidth()) {
                    distanceX = rect.right - mTextureView.getWidth();
                }
            }
            return mTextureView.getPivotX() + distanceX * (1 / (currentScaleFactor - 1));
        }

        private float checkScrollY(RectF rect, float distanceY) {
            if (rect.height() <= getHeight() || distanceY == 0) {
                return mTextureView.getPivotY();
            }
            if (distanceY < 0) {
                if (rect.top - distanceY > 0)
                    distanceY = rect.top;
            } else if (distanceY > 0) {
                if (rect.bottom - distanceY < mTextureView.getHeight()) {
                    distanceY = rect.bottom - mTextureView.getHeight();
                }
            }
            return mTextureView.getPivotY() + distanceY * (1 / (currentScaleFactor - 1));
        }

        /**
         * 计算双击时放大的缩放因子
         */
        private float computeDoubleTapScale() {
            if (!Float.isNaN(doubleTapScaleFactor)) {
                return doubleTapScaleFactor;
            }
            if (mTextureView.getHeight() < getHeight()) {
                return getHeight() / (float) mTextureView.getHeight();
            }
            return DOUBLE_TAP_SCALE_DEFAULT;
        }

        private void createScaleAnimator(float start, final float end) {
            scaleAnimator = ObjectAnimator.ofFloat(null, "", start, end);
            scaleAnimator.setDuration(doubleTapScaleDuration);
            scaleAnimator.setInterpolator(new LinearInterpolator());
            scaleAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
                @Override
                public void onAnimationUpdate(ValueAnimator animation) {
                    setInternalScale((float) animation.getAnimatedValue());
                }
            });
            scaleAnimator.addListener(new AnimatorListenerAdapter() {
                @Override
                public void onAnimationEnd(Animator animation) {
                    if (end == 1) {
                        resetInternalPivot();
                    }
                    currentScaleFactor = mTextureView.getScaleX();
                    isScaling = false;
                }

                @Override
                public void onAnimationStart(Animator animation) {
                    isScaling = true;
                }
            });
        }
    }

    public interface OnVideoPlayerViewListener {

        void onSurfaceTextureAvailable(SurfaceTexture surface);

        void onSurfaceTextureDestroyed(SurfaceTexture surface);

        void onSurfaceTextureUpdated();
    }
}
